import * as chokidar from 'chokidar'
import * as path from 'path'
import * as fs from 'fs'

type Page = {
  path: string
  parent: string
  element?: string
  children: Page[]
}

const toPath = function (str: string) {
  return `'/${str}'`
}

export default class RouteWatcher {

  folder: string
  target: string = path.resolve('./src/router/course.tsx')
  options: chokidar.WatchOptions
  watcher: chokidar.FSWatcher

  routes: Page[] = []
  map: Record<string, string> = {}

  timer: NodeJS.Timeout | null = null
  template: string = fs.readFileSync(__dirname + '\\router.tpl', { encoding: 'utf-8' })

  constructor(folder: string = './src/views', options: chokidar.WatchOptions = {}) {
    this.folder = folder
    this.options = options
  }

  start = () => {
    console.log('[plugins] start create routes ...')

    this.watcher = chokidar.watch(this.folder, {
      ignored: /dialog/,
      persistent: true,
      ...this.options
    })
    this.watcher
      .on('add', this.addRoute)
      .on('unlink', this.removeRoute)
  }

  close = () => {
    this.watcher?.close()
  }

  find(path: string, list?: Page[]): {
    target: Page,
    parent: Page[],
    index: number
  } | void {
    list = list || this.routes
    for (let i = 0; i < list.length; i++) {
      if (list[i].path === path) {
        return {
          target: list[i],
          parent: list,
          index: i
        }
      } else {
        const result = this.find(path, list[i].children)
        if (result) {
          return result
        }
      }
    }
  }

  addRoute = (path: string) => {
    const pick = /^src\\views\\(.*?)\.tsx?$/.exec(path)

    if (pick) {
      const dirs = pick[1].split('\\')
      const fileName = dirs.pop()
      if (/^(table|index)$/.test(fileName)) {
        const basePath = dirs.join('/')
        let page: Page = {
          path: toPath(basePath),
          parent: dirs.pop() && toPath(dirs.join('/')),
          element: '',
          children: []
        }
        if (fileName === 'index') {
          page.element = `(
            <CourseView
              cacheKey={'${basePath}'}
              view={React.lazy(() => import('../views/${basePath}'))} 
            />
          )`
        } else if (fileName === 'table') {
          page.element = `( 
            <CourseView
              cacheKey={'${basePath}'}
              table={() => import('../views/${basePath}/table')}  
              schema={() => import('../views/${basePath}/schema')} 
              service={() => import('../views/${basePath}/service')}
            />
          )`
        }
        for (let count = 0; count < 10; count++) {
          const result = this.find(page.parent)
          if (result) {
            result.target.children.push(page)
            if (!this.timer) {
              this.timer = setTimeout(() => {
                this.generate()
                this.timer = null
              }, 1000)
            }
            break;
          } else {
            if (dirs.pop()) {
              const child = { ...page }
              page = {
                path: child.parent,
                parent: toPath(dirs.join('/')),
                children: [child]
              }
            } else {
              this.routes.push(page)
              break;
            }
          }
        }
      }
    }
  }

  removeRoute = (path: string) => {
    const pick = /^src\\views\\(.*?)\.tsx?$/.exec(path)

    if (pick) {
      const dirs = pick[1].split('\\')
      const fileName = dirs.pop()
      if (fileName === 'table' || fileName === 'index') {
        const basePath = dirs.join('/')
        const result = this.find(toPath(basePath))
        if (result) {
          const { parent, index } = result
          parent.splice(index, 1)
          if (!this.timer) {
            this.timer = setTimeout(() => {
              this.generate()
              this.timer = null
            }, 1000)
          }
        }
      }
    }
  }

  generate = () => {

    const content = JSON.stringify(this.routes, null, '\t')
      .replace(/\"/g, '')
      .replace(/\\n/g, '\n')

    const router = this.template.replace('${routes}', content)
    fs.writeFileSync(this.target, router, { encoding: 'utf-8' })

    console.log('[plugins] successful create routes!')
  }
}
